﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace Aeon.Framework
{

/// <summary>
/// Multiton abstract factory for World dependency injection.
/// 
/// </summary>
public static class WorldManager
    {
    // --- Attributes:
       
    private static Dictionary<String,World> worlds = new Dictionary<String,World>();        // World key-value pairs.
    private static Boolean modules = false;                                                 // Modules set flag.
    
    private static Type typeTextures = null;
    private static Type typeFonts = null;
    private static Type typeAudio = null;
    private static Type typeInput = null;
    private static Type typePhysics = null;

    // --- Public Methods:
    
    /// <summary>
    /// Sets which component modules new Worlds will use.
    /// Returns false if specified module types are not valid.
    /// 
    /// </summary>
    /// <param name="texture"> Texture Manager module class. </param>
    /// <param name="font"> Font Manager module class. </param>
    /// <param name="audio"> Audio Manager module class. </param>
    /// <param name="input"> Input Manager module class. </param>
    /// <param name="physics"> Physics Manager module class. </param>
    public static bool SetModules(Type texture, Type font, Type audio, Type input, Type physics)
        {
        TypeFilter validFilter = new TypeFilter(compareInterfaces);
        
        String[] validInterfaces = { typeof(ITextureManager).FullName,
                                     typeof(IFontManager).FullName,
                                     typeof(IAudioManager).FullName,
                                     typeof(IInputManager).FullName, 
                                     typeof(IPhysicsManager).FullName };
        
        if (texture.FindInterfaces(validFilter,validInterfaces[0]).Length > 0) { typeTextures = texture; }
        if (font.FindInterfaces(validFilter,validInterfaces[1]).Length > 0) { typeFonts = font; }
        if (audio.FindInterfaces(validFilter,validInterfaces[2]).Length > 0) { typeAudio = audio; }
        if (input.FindInterfaces(validFilter,validInterfaces[3]).Length > 0) { typeInput = input; }
        if (physics.FindInterfaces(validFilter,validInterfaces[4]).Length > 0) { typePhysics = physics; }
        
        modules = (typeTextures != null && typeFonts != null && typeAudio != null && typeInput != null && typePhysics !=null );
        
        return(modules);
        }
        
    /// <summary>
    /// Creates a new world using specified modules or returns identified world.
    /// Returns null if modules are not previously set with SetModules.
    /// Requires an active OpenGL context.
    /// 
    /// </summary>
    /// <param name="world"> World name identifier. </param>    
    public static World CreateWorld(String world)
        {
        World newWorld = null;
        
        if (modules)
           {
           if (!worlds.TryGetValue(world, out newWorld))
              {
              try {
                  newWorld = new World(world);
                  
                  newWorld.InjectTexture = Activator.CreateInstance(typeTextures) as ITextureManager;              
                  newWorld.InjectFont = Activator.CreateInstance(typeFonts) as IFontManager;
                  newWorld.InjectAudio = Activator.CreateInstance(typeAudio) as IAudioManager;
                  newWorld.InjectInput = Activator.CreateInstance(typeInput) as IInputManager;
                  newWorld.InjectPhysics = Activator.CreateInstance(typePhysics) as IPhysicsManager;
                  
                  newWorld.CreateScene();
                  }
              
              catch(ArgumentNullException){ newWorld = null; }
              }
          }
        
        return(newWorld);
        }
    
    /// <summary>
    /// Finalizes the World/Context for that identifier, if found.
    /// 
    /// </summary>
    /// <param name="world"> Name identifier of world to be removed. </param>
    public static Boolean RemoveWorld(String world)
        {
        if (worlds.ContainsKey(world))
            {
            worlds[world].DestroyScene();
            worlds.Remove(world);
            return(true);
            }
        
        else return(false);
        }
        
    // --- Private Methods:
    
    private static bool compareInterfaces(Type type, Object criteria)
        {
        if(type.ToString() == criteria.ToString()) return true;
        else return false;
        }

    }
    
    
}
